/**
 * 
 */
package org.vsg.stock.extension.service.impl;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocCollector;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.vsg.common.exceptions.service.ServiceException;
import org.vsg.stock.basic.algorithm.AnalysisAlgorithm;
import org.vsg.stock.basic.domain.StockDailyLine;
import org.vsg.stock.basic.persistence.StockDailyLineDao;
import org.vsg.stock.extension.service.DailyContentHandler;
import org.vsg.stock.extension.service.StockHistoryService;
import org.vsg.stock.extension.vo.ExtStockDailyLineVO;
import org.vsg.stock.extension.vo.HistoryPatternStock;
import org.vsg.stock.extension.vo.LinkageStocksItemVO;

/**
 * @author vison
 *
 */
public class StockHistoryServiceImpl implements StockHistoryService , InitializingBean{

	/**
	 * set the logger for develope code , do not use System.print(ln) to out print message.
	 */
	private static final Logger logger = LoggerFactory.getLogger(StockHistoryServiceImpl.class);	
	
	@Resource
	private StockDailyLineDao stockDailyLineDao;
	
	private File indexDir;

	private File dataDir;
	
	private File linkageStocksDir;
	
	private SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");	

	public File getIndexDir() {
		return indexDir;
	}

	public void setIndexDir(File indexDir) {
		this.indexDir = indexDir;
	}

	public File getDataDir() {
		return dataDir;
	}

	public void setDataDir(File dataDir) {
		this.dataDir = dataDir;
	}

	public File getLinkageStocksDir() {
		return linkageStocksDir;
	}

	public void setLinkageStocksDir(File linkageStocksDir) {
		this.linkageStocksDir = linkageStocksDir;
	}

	/* (non-Javadoc)
	 * @see org.vsg.stock.custcomp.service.StockHistoryService#findHistoryPattern(java.lang.String)
	 */
	@Override
	public Map<String , List<HistoryPatternStock>> findHistoryPattern(String pattern )
			throws ServiceException {
		// TODO Auto-generated method stub
		if (pattern == null || pattern.equals("")) {
			throw new ServiceException("Find The History Pattern is not null! Please reset it !");
		}
		Map<String , List<HistoryPatternStock>> hpsMap = new HashMap<String,List<HistoryPatternStock>>();		
		// --- the first core processor ---
		
		BooleanQuery query = new BooleanQuery();
		query.add(new PrefixQuery(new Term("shapePattern" , pattern)) , BooleanClause.Occur.SHOULD);
		query.add(new WildcardQuery(new Term("shapePattern" , "*"+pattern + "*")) , BooleanClause.Occur.SHOULD);
		
		IndexSearcher isearcher = null;

		try {
			isearcher = new IndexSearcher( FSDirectory.getDirectory( indexDir ) );
			TopDocCollector collector = new TopDocCollector(10000);
			
			isearcher.search( query, collector );
			ScoreDoc[] hits = collector.topDocs().scoreDocs;

			Document doc = null;

			for (int i = 0 ; i < hits.length ; i++) {
				doc = isearcher.doc( hits[i].doc );
				
				String content = doc.get("shapePattern");
				String stockCode = doc.get("stockCode");
				String detailContent = doc.get("detailShapePattern");
				
				int position = 0;
				int index = 0;
				while ( (position = content.indexOf(pattern)) > -1) {
					//System.out.println( "defined position : " + position + " , " + pattern);
					List<HistoryPatternStock> allPatternStocks = genHistoryPatternStockList(dataDir, content , detailContent , stockCode ,pattern );
					hpsMap.put(stockCode+"-"+index, allPatternStocks);
					content = content.substring(position + pattern.length() );
					index++;
				}
				

			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (isearcher != null) {
				try {
					isearcher.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					isearcher = null;
				}
			}
		}		

		return hpsMap;
	}
	
	private List<HistoryPatternStock> genHistoryPatternStockList(File dataDir, String content , String detailContent , String code , String pattern) {
		//File stockDataFile = new File(dataDir , code+".txt");

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		String[] detailLines = detailContent.split("#");
		
		List<HistoryPatternStock> allPatternStocks = new LinkedList<HistoryPatternStock>();
	
		int position = content.indexOf(pattern);
		
		InputStream is = null;
		
		if (position != -1 ) {
			
			try {
				// --- get the shape pattern data ---

				char[] patternCharArray = pattern.toCharArray();
				for (int i = 0 ; i < patternCharArray.length ; i++) {
					String detailLine = detailLines[position+i];
					String[] pwords = detailLine.split(":");
					String[] argus = pwords[1].split(",");
					
					HistoryPatternStock patternStock = new HistoryPatternStock();
					patternStock.setCode( code );
					patternStock.setStartDate( sdf.parse( argus[1] ) );
					patternStock.setEndDate( sdf.parse( argus[2] ) );
					patternStock.setCycle(Integer.parseInt( argus[3] ));
					patternStock.setRatioValue( Double.parseDouble( argus[0] ) );
					allPatternStocks.add( patternStock );
			
				}

			} catch (ParseException e) {
				e.printStackTrace();
			} finally {
				if (is != null) {
					try {
						is.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally {
						is = null;
					}
				}
			}
		}
		return allPatternStocks;		
	}

	@Override
	public String getAbstractShapePattern(List<StockDailyLine> lines , int minPeriodicity) {
		// TODO Auto-generated method stub
		StockDailyLine firstEnt = lines.get(0);
		StockDailyLine lastEnt = lines.get( lines.size() - 1 );
		String stockCode = firstEnt.getStockCode();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

		BooleanQuery query = new BooleanQuery();
		query.add(new TermQuery(new Term("stockCode" , stockCode.toLowerCase())) , BooleanClause.Occur.SHOULD);
	
		IndexSearcher isearcher = null;
		
		StringBuilder shapeSign = new StringBuilder();
		StringBuilder minShapeSign = new StringBuilder();
		try {
			isearcher = new IndexSearcher( FSDirectory.getDirectory( indexDir ) );
			TopDocCollector collector = new TopDocCollector(10000);
			
			isearcher.search( query, collector );
			ScoreDoc[] hits = collector.topDocs().scoreDocs;

			Document doc = null;
			
			
			boolean begin = false;
			int icount = 0;
			boolean minBegin = false;
			Date startDate = null;
			Date endDate = null;

			for (int i = 0 ; i < hits.length ; i++) {
				doc = isearcher.doc( hits[i].doc );

				String detailContent = doc.get("detailShapePattern");
				String[] detailLines = detailContent.split("#");
				for (int j = 0 ; j < detailLines.length ; j++) {
					String detailLine = detailLines[j];
					String[] pwords = detailLine.split(":");
					String[] argus = pwords[1].split(",");
					
					try {
						
						startDate = sdf.parse( argus[1]);
						endDate = sdf.parse( argus[2] );
						int cycle = Integer.parseInt( argus[3] );
						double ratioValue =  Double.parseDouble( argus[0] );
						
						if (startDate.compareTo( firstEnt.getDate() )<= 0 
								&& endDate.compareTo( firstEnt.getDate() ) > 0) {
							begin = true;
							minBegin = true;
						}
						if ( endDate.compareTo( lastEnt.getDate() ) <=0) {
							begin = false;
						}
				
						if (begin) {
							shapeSign.append(pwords[0]);							
						}
						if (minBegin && icount < minPeriodicity) {
							minShapeSign.append(pwords[0]);
							icount++;
						}

						
					} catch (NumberFormatException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (ParseException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (isearcher != null) {
				try {
					isearcher.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					isearcher = null;
				}
			}
		}

		String outputShapeSign = shapeSign.toString().length() >= minPeriodicity? shapeSign.toString(): minShapeSign.toString();

		return outputShapeSign;
	}	

	@Override
	public List<HistoryPatternStock> getAbstractShapePatternData(
			List<StockDailyLine> lines, int minPeriodicity) {
		// TODO Auto-generated method stub
		StockDailyLine firstEnt = lines.get(0);
		StockDailyLine lastEnt = lines.get( lines.size() - 1 );
		String stockCode = firstEnt.getStockCode();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		BooleanQuery query = new BooleanQuery();
		query.add(new TermQuery(new Term("stockCode" , stockCode.toLowerCase())) , BooleanClause.Occur.SHOULD);
	
		IndexSearcher isearcher = null;
		List<HistoryPatternStock> allPatternStock = new LinkedList<HistoryPatternStock>();
		List<HistoryPatternStock> bakAllPatternStock = new LinkedList<HistoryPatternStock>();		
		try {
			isearcher = new IndexSearcher( FSDirectory.getDirectory( indexDir ) );
			TopDocCollector collector = new TopDocCollector(10000);
			
			isearcher.search( query, collector );
			ScoreDoc[] hits = collector.topDocs().scoreDocs;

			Document doc = null;
			
			HistoryPatternStock patternStock  = null;
			
			boolean begin = false;
			int icount = 0;
			boolean minBegin = false;
			for (int i = 0 ; i < hits.length ; i++) {
				doc = isearcher.doc( hits[i].doc );

				String detailContent = doc.get("detailShapePattern");
				String[] detailLines = detailContent.split("#");
				for (int j = 0 ; j < detailLines.length ; j++) {
					String detailLine = detailLines[j];
					String[] pwords = detailLine.split(":");
					String[] argus = pwords[1].split(",");
					
					patternStock = new HistoryPatternStock();
					try {
						patternStock.setCode( stockCode );
						patternStock.setStartDate( sdf.parse( argus[1] ) );
						patternStock.setEndDate( sdf.parse( argus[2] ) );
						patternStock.setCycle(Integer.parseInt( argus[3] ));
						patternStock.setRatioValue( Double.parseDouble( argus[0] ) );
						
						
						if (patternStock.getStartDate().compareTo( firstEnt.getDate() )<= 0 
								&& patternStock.getEndDate().compareTo( firstEnt.getDate() ) > 0) {
							begin = true;
							minBegin = true;
						}
						if ( patternStock.getEndDate().compareTo( lastEnt.getDate() ) <=0) {
							begin = false;
						}
				
						if (begin) {
							allPatternStock.add( patternStock );							
						}
						if (minBegin && icount < minPeriodicity) {
							bakAllPatternStock.add( patternStock );
							icount++;
						}

						
					} catch (NumberFormatException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (ParseException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (isearcher != null) {
				try {
					isearcher.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					isearcher = null;
				}
			}
		}
		List<HistoryPatternStock> outputPatternStock = allPatternStock.size() >= minPeriodicity ? allPatternStock : bakAllPatternStock;
		return outputPatternStock;
	}
	

	@Override
	public String convertToHistoryPattern(String stockCode, Date startDate,
			int cycle) throws ServiceException {
		// TODO Auto-generated method stub
		return null;
	}	
	
	
	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub

		
	}

	@Override
	public void buildStockPatternIndex(File orgDir, File targetIndexDir)
			throws ServiceException {
		// TODO Auto-generated method stub
		IndexWriter indexWriter  = null;
		try {
			indexWriter = getFileIndexWriter(targetIndexDir);

			addStockDataToIndex(indexWriter , orgDir);

			indexWriter.optimize();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {

			try {

				if (indexWriter != null) {
					indexWriter.close();
				}
		
			} catch (CorruptIndexException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			} finally {
				indexWriter = null;
			}			
			
		}
		
	}

	private IndexWriter getFileIndexWriter(File indexDir) throws IOException{
		
		Directory dir = FSDirectory.getDirectory(indexDir);
		
		StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
		
		IndexWriter indexWriter = new IndexWriter(
				dir, standardAnalyzer, true,
				IndexWriter.MaxFieldLength.UNLIMITED);
		
		return indexWriter;
	}
	
	private void addStockDataToIndex(IndexWriter writer , File orginalDataDir) throws IOException {
		File[] stockFiles = orginalDataDir.listFiles();
		
		File sfile = null;

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");		
		
		for (int i = 0 ; i < stockFiles.length ; i++) {
			sfile = stockFiles[i];
			
			if ( sfile.isDirectory() ) {
				continue;
			}
			
			Document doc = new Document();
			
			String stockCode = sfile.getName().substring(0,8);
			
			// --- add the key field ---
			Field pkfield = new Field("stockCode" , stockCode , Field.Store.YES , Field.Index.ANALYZED);
			doc.add(pkfield);
			
			BufferedInputStream bis = null;
			StringBuilder qualitativeHisContent = new StringBuilder();
			try {
				List<String> content = IOUtils.readLines(new BufferedInputStream( new FileInputStream(sfile) ) );
				
				// --- add the data start date ---
				if (content.size() > 0) {
					String line = content.get(0);
					String[] lineData = line.split(",");
					String dateStr = lineData[1];					
					Field datefield = new Field("startDate" , dateStr , Field.Store.YES , Field.Index.ANALYZED);
				}

				for (int j = 0 ; j < content.size() ; j++) {
					String line = content.get(j);
					String[] lineData = line.split(",");

					double yieldRate =  Double.parseDouble( lineData[8] );
					qualitativeHisContent.append( AnalysisAlgorithm.changeAbstractValue(yieldRate) );

				}
				
				// --- add the content --- to the field ---
				Field cotentField = new Field("patternField" , qualitativeHisContent.toString() , Field.Store.YES , Field.Index.ANALYZED);
				doc.add( cotentField );

			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (bis != null) {
					try {
						bis.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally {
						bis = null;
					}
				}
			}
			
			try {
				
				writer.addDocument( doc );
				
			} catch (CorruptIndexException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}
	
	
	private void addAverageLineShapeToIndex(IndexWriter writer , File orginalDataDir , int cycle) {
		File[] stockFiles = orginalDataDir.listFiles();
		
		File sfile = null;

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");		
		
		for (int i = 0 ; i < stockFiles.length ; i++) {
			sfile = stockFiles[i];
			
			if ( sfile.isDirectory() ) {
				continue;
			}
			
			Document doc = new Document();
			
			String stockCode = sfile.getName().substring(0,8);

			// --- add the key field ---
			Field pkfield = new Field("stockCode" , stockCode.toLowerCase() , Field.Store.YES , Field.Index.ANALYZED);
			doc.add(pkfield);
			
			BufferedInputStream bis = null;
			StringBuilder qualitativeHisContent = new StringBuilder();
			try {
				List<String> content = IOUtils.readLines(new BufferedInputStream( new FileInputStream(sfile) ) );
				
				// --- add the data start date ---
				Collections.reverse( content );

				DailyContentHandler handler = new DailyContentHandler();
				handler.handle( content , stockCode );
				String shapeSign = handler.getShapeSign();

				// --- add the content --- to the field ---
				Field cotentField = new Field("shapePattern" , shapeSign, Field.Store.YES , Field.Index.ANALYZED);
				doc.add( cotentField );				
				
				String detailShapeSign = handler.getDetailSign();
				
				// --- add the content --- to the field ---
				Field detailField = new Field("detailShapePattern" , detailShapeSign, Field.Store.YES , Field.Index.ANALYZED);
				doc.add( detailField );				

				
		} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (bis != null) {
					try {
						bis.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} finally {
						bis = null;
					}
				}
			}
			
			try {
				
				writer.addDocument( doc );
				
			} catch (CorruptIndexException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}		
	}

	@Override
	public void buildAverageLineShape(File orgDir, File targetIndexDir , int cycle)
			throws ServiceException {
		// TODO Auto-generated method stub
		
		IndexWriter indexWriter  = null;
		try {
			indexWriter = getFileIndexWriter(targetIndexDir);

			addAverageLineShapeToIndex(indexWriter , orgDir , cycle);

			indexWriter.optimize();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {

			try {

				if (indexWriter != null) {
					indexWriter.close();
				}
		
			} catch (CorruptIndexException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			} finally {
				indexWriter = null;
			}			
			
		}		
		
	}

	@Override
	public List<LinkageStocksItemVO> findLinkageStocksItemByDate(Date date)
			throws ServiceException {
		// TODO Auto-generated method stub
		String dateStr = dateFormat1.format(date);
		File file = new File(this.linkageStocksDir , dateStr+".txt");
		
		List<LinkageStocksItemVO> itemList = new Vector<LinkageStocksItemVO>();
		
		if (!file.exists()) {
			return itemList;
		}
		
		InputStream is = null;
		try {
			is = new FileInputStream(file);
			List lines = IOUtils.readLines(is);
			String line = null;
			LinkageStocksItemVO itemVO = null;
			for (Iterator<String> linesIter = lines.iterator() ; linesIter.hasNext();) {
				line = linesIter.next();
				String[] words = line.split(",");
				itemVO = new LinkageStocksItemVO();
				itemVO.setCategoryId( words[0] );
				itemVO.setDescription( words[1] );
				itemVO.setStockCount( Integer.parseInt( words[2] ) );
				itemVO.setAvgRatio( Double.parseDouble( words[3] )) ;
				itemVO.setType( words[4] );
				itemVO.setStockCodes( words[5].split("\\|") );
				itemList.add(itemVO);
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return itemList;
	}

	@Override
	public List<String> findLinkageStocksDescByDate(Date date)
			throws ServiceException {
		// TODO Auto-generated method stub
		String dateStr = dateFormat1.format(date);
		File file = new File(this.linkageStocksDir , dateStr+".txt");
		
		List<String> itemList = new Vector<String>();
		
		if (!file.exists()) {
			return itemList;
		}
		
		InputStream is = null;
		try {
			is = new FileInputStream(file);
			List lines = IOUtils.readLines(is);
			String line = null;
			LinkageStocksItemVO itemVO = null;
			for (Iterator<String> linesIter = lines.iterator() ; linesIter.hasNext();) {
				line = linesIter.next();
				String[] words = line.split(",");
				itemList.add(words[0]);
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return itemList;
	}
	

	@Override
	public List<ExtStockDailyLineVO> findOverRatioExtStockDailyLineByDate(
			Date refDate, double lowerOverRatio) throws ServiceException {
		// TODO Auto-generated method stub
		List<StockDailyLine> stkDailyLines = this.stockDailyLineDao.findStockLineByOverRatio( refDate , lowerOverRatio);
		
		List<ExtStockDailyLineVO> extStkDailyLines = new ArrayList<ExtStockDailyLineVO>();
		for (Iterator<StockDailyLine> stkDailyIter = stkDailyLines.iterator() ; stkDailyIter.hasNext();) {
			extStkDailyLines.add( new ExtStockDailyLineVO(stkDailyIter.next()) );
		}
		return extStkDailyLines;
	}
	


}
